package io.iot.pipeline.decoder;

import io.iot.pipeline.CRC16;
import io.iot.pipeline.model.OLikeProtocol;
import io.iot.pipeline.model.WaterMeter;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufProcessor;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.util.ReferenceCountUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;

/**
 * 第三方解析解析处理器（应该包含水表与电表）
 * @author cary
 */
public class OLikeProtocolDecoder extends ByteToMessageDecoder {
    private static Log log = LogFactory.getLog(OLikeProtocolDecoder.class);

    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        msg = dropStartAndEnd(msg);

        boolean isCRC16Valid = isCRC16Valid(msg);

        if (!isCRC16Valid) {
            log.info("CRC16 未校验通过!");
            return;
        }

        OLikeProtocol result = letsParse(msg);
        out.add(result);

        msg.clear();
    }

    /**
     * 开始解析协议
     * @param byteBuf
     */
    private OLikeProtocol letsParse(ByteBuf byteBuf) {
        byteBuf.resetReaderIndex();
        int p1 = byteBuf.readBytes(2).order(ByteOrder.LITTLE_ENDIAN).readChar(); // 集中器编号 2字节
        int p2 = byteBuf.readBytes(2).order(ByteOrder.LITTLE_ENDIAN).readChar(); // 集抄器编号 2字节

        byte flag = byteBuf.readByte(); // 标记

        int p3 = byteBuf.readBytes(2).order(ByteOrder.LITTLE_ENDIAN).readChar(); // 协议编码
        byte p4 = byteBuf.readByte(); // 楼号
        byte p5 = byteBuf.readByte(); // 单元
        byte p6 = byteBuf.readByte(); // 楼层
        byte p7 = byteBuf.readByte(); // 房间号
        byte p8 = byteBuf.readByte(); // 采集器编号
        char p9 = byteBuf.readChar(); // 采集时间

        // 采集时间方法未知..
        int day = p9 & 0xf;
        int month = p9 & 0xf0;
        int year = (p9 & 0x7f00);

        System.out.printf("%d , %d , %d\n",day,month,year);

        OLikeProtocol oLikeProtocol = new OLikeProtocol();
        oLikeProtocol.setCtrlNo(p1);
        oLikeProtocol.setCounterNo1(p2);
        oLikeProtocol.setCounterNo2(p8);
        oLikeProtocol.setpNo(p3);
        oLikeProtocol.setBuildingNo(p4);
        oLikeProtocol.setUnitNo(p5);
        oLikeProtocol.setFloorNo(p6);
        oLikeProtocol.setRoomNo(p7);
        oLikeProtocol.setTime(p9);

        if (flag == 0x04) {
            // 水表协议
            oLikeProtocol.setData(letsParseWaterMeter(byteBuf,oLikeProtocol));
        } else if (flag == 0x02) {
            // 电表协议
        }

        return oLikeProtocol;
    }

    protected WaterMeter letsParseWaterMeter(ByteBuf byteBuf,OLikeProtocol oLikeProtocol) {
        int startIndex = byteBuf.forEachByte(new ByteBufProcessor() {
            public boolean process(byte value) throws Exception {
                return value == 0x68;
            }
        });

        int endIndex = byteBuf.forEachByteDesc(new ByteBufProcessor() {
            public boolean process(byte value) throws Exception {
                return value == 0x16;
            }
        });

        byteBuf.readerIndex(startIndex - 1);

        byteBuf = byteBuf.readBytes(endIndex - startIndex);

        byteBuf.readByte(); // 68 起始符
        byteBuf.readByte(); // 10 仪表类型

        String p1 = ByteBufUtil.hexDump(byteBuf.readBytes(7)); //表地址

        byteBuf.readBytes(5);

        String p2 = ByteBufUtil.hexDump(byteBuf.readBytes(4)); //表读数

        byteBuf.readBytes(1); // 2C 分隔符

        byte st = byteBuf.readByte(); // 00 阀门状态&电池电压

        double p3 = (double)(byteBuf.readByte() & 0xFF) / 100; // 电压

        WaterMeter waterMeter = new WaterMeter();
        waterMeter.setId(p1);
        waterMeter.setValue(p2);
        waterMeter.setTension(p3);
        waterMeter.setBatteryState(WaterMeter.parseBatteryState(st));
        waterMeter.setValveState(WaterMeter.parseValveState(st));
        return waterMeter;
    }

    /**
     * 去除粘包符号（0x7e）标记头尾
     * @param msg
     * @return
     */
    protected ByteBuf dropStartAndEnd(ByteBuf msg) {
        if(!(msg.isReadable() && msg.getByte(0) == 0x7e)) {
            return msg;
        }
        msg.readByte(); // 0x7e
        ByteBuf byteBuf = msg.readBytes(msg.readableBytes() - 1);
        msg.readByte(); // 0x7e
        return byteBuf;
    }

    /**
     * CRC16 校验方式
     * @param msg
     * @return
     */
    protected boolean isCRC16Valid(ByteBuf msg) {
        if(!msg.isReadable()) {
            return false;
        }

        msg.resetReaderIndex();
        byte[] bytes = new byte[msg.readableBytes() - 2];
        msg.readBytes(bytes);

        int v = CRC16.decode(bytes,0,bytes.length);
        int lastA = v / 256;
        int lastB = v % 256;
        int lastAOfByte = msg.readByte() & 0xFF;
        int lastBOfByte = msg.readByte() & 0xFF;
        return lastA == lastAOfByte && lastB == lastBOfByte;
    }

    public static void main(String[] args) {
        String rString = "7E b6 33 07 00 04 01 4c 05 02 0e 01 26 22 33 78 " +
                "68 10 00 00 20 16 11 01 35 81 0A 90 1F 00 00 00 39 00 2C 00 92 26 16 46 84 7E";

//        String rString = "7E B6 33 07 00 04 01 4C 05 02 0B 01 26 22 14" +
//                "68 10 00 00 16 09 01 83 85 81 0A 90 1F 00 00 00 00 90 2C 00 89 1F 16 D6 EE 7e";
        rString = rString.replace(" ","");

        byte[] bytes = ByteBufUtil.decodeHexDump(rString);
        ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes);

        try {
            new OLikeProtocolDecoder().decode(null,byteBuf,new ArrayList<Object>());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
